<template>
  <div class="gis-map-component">
    <!-- 地图渲染根 -->
    <div :id="idName" ref="mapBox" class="map-class"></div>
    <div v-show="mapLoading" class="layer-loading">
      <a-spin size="large" :delay="500" tip="Loading..."></a-spin>
    </div>
    <!-- 搜索 -->
    <k-gis-search
      :visible="currentToolBarAction === 'gisSearchVisible'"
      :top-margin="collapseTool"
      @fit-bounds="mapBoxFitBoundsMixin"
      @search-entity="searchEntity"
      @open-entity="saveAndOpenEntity"
      @close-search="closeSearch"
    />
    <!-- 图层 -->
    <k-gis-layer
      :visible="currentToolBarAction === 'gisLayer'"
      @update-layer-order="updateLayerOrderEmit"
      @toggle-layer-visible="toggleLayerVisibleEmit"
      @cancel-layer-fixed="cancelLayerFixedEmit"
    />
    <!-- 实体名称设置 -->
    <entity-name-set
      :visible="currentToolBarAction === 'nameSet'"
      :name-set="nameSet"
      :interact-graph="interactGraph"
      :top-margin="collapseTool"
      @name-set-change="changeNameSet"
      @interact-graph-change="changeInterGraph"
    />
    <!-- gis config -->
    <gis-config-panel
      :visible="currentToolBarAction === 'gisSetting'"
      :current-map-config="gisInfoMapTypeStyle"
      @setting="updateSetting"
    />
    <!-- entity detail -->
    <!-- 循环生成，若没有位置信息，根据前者叠加向左偏移 -->
    <div v-if="entityList.length > 0">
      <k-entity-panel
        v-for="entity in entityList"
        :key="entity.id"
        :ref="`entity-${entity.id}`"
        :source-data="entity.layout"
        @open-entity="openOtherEntity"
        @reset-entity="resetEntity"
        @show-fixed="showFixedLineLayer"
        @hide-fixed="removeFixedLineLayer"
        @close-entity="closeEntityPanel"
      />
    </div>
    <!-- chart config panel -->
    <k-chart-config :visible="currentGridItem" />
    <!-- text list  -->
    <div v-if="textList.length > 0">
      <common-text-editor
        v-for="textItem in textList"
        :key="textItem.i"
        :text-item="textItem"
        @delete-text="deleteText(textItem)"
        @editor-blur="handleTextBlur(textItem)"
        @update-size="(param) => handleTextSize(textItem, param)"
        @update-position="(param) => handleTextPos(textItem, param)"
        @text-change="
          (val) => {
            handleTextChange(textItem, val)
          }
        "
      >
      </common-text-editor>
    </div>
  </div>
</template>

<script lang="ts">
/**
 * gis 地图画布
 */
import { Component, Watch, Mixins, Prop } from 'vue-property-decorator'
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'
import { getMapTilesByMapTypeAndStyle } from '@/components/knowledge-gis/map-packages/map-tiles'
import KGraphStore from '@/store/modules/k-graph'
import KGisSearch from '@/components/knowledge-gis/KGisSearch.vue'
import KGisLayer from '@/components/knowledge-gis/KGisLayer.vue'
import EntityNameSet from '@/components/knowledge-gis/EntityNameSet.vue'
import GisConfigPanel from '@/components/knowledge-gis/GisConfigPanel.vue'
import KEntityPanel from '@/components/knowledge-gis/KEntityPanel.vue'
import KChartConfig from '@/components/knowledge-gis/KChartConfig.vue'
import {
  globalHoverSetFeatureState,
  hoverSetFeatureState,
  selectedSetFeatureState,
  clearSelectedFeatureState,
} from '@/components/gis/map-package/map-utils/highlight-util'
import KnowledgeGisMapMixin from '@/components/knowledge-gis/knowledge-gis-map-mixin'
import './map-style.less'
import { GisInfo } from '@/interface/k-graph-interface'
import {
  resetPanelInfoByVid,
  updatePanelInfoByPanelId,
} from '@/api/knowledge-graph'
import CommonTextEditor from '@/components/common/CommonTextEditor/CommonTextEditor.vue'
import { buildSaveParameters } from '@/util/common'
import { getWidgetData, updateWidget } from '@/api/widget'
import { cloneDeep } from 'lodash'
import { key } from '@/components/gis/map-package/map-config'
import DataViewStore from '@/store/modules/dataview'

@Component({
  components: {
    KGisSearch,
    KGisLayer,
    EntityNameSet,
    GisConfigPanel,
    KEntityPanel,
    KChartConfig,
    CommonTextEditor,
  },
})
export default class KnowledgeGisMap extends Mixins(KnowledgeGisMapMixin) {
  @Prop({ type: String, default: 'map' }) idName!: string
  @Prop({ type: Boolean, default: false }) collapseTool!: boolean //  是否为收缩toolbar case

  // 返回地图当前的缩放级别
  public get getZoom() {
    return this.mapBox ? this.mapBox.getZoom() : 0
  }
  public projectId: number | null = null // 项目 id
  public drag: any = null
  public panelWidth: number | null = null
  public panelHeight: number | null = null
  public panelTop: number | null = null
  public panelRight: number | null = null

  // 项目锁定
  public get isLock() {
    return DataViewStore.getLock
  }

  public get nameSet() {
    return KGraphStore.sysSetting.nameSet
  }

  public get interactGraph() {
    return KGraphStore.sysSetting.interactGraph
  }

  //  text start
  public get textList() {
    return KGraphStore.textList || []
  }

  getTextData(gridItem: any) {
    if (gridItem && gridItem.chartOptions && gridItem.chartType === 'text') {
      const dataSend: any = {
        data: {
          id: gridItem.widgetId,
          type: gridItem.chartType, //  'text'
        },
      }
      this.$set(gridItem, 'isLoading', true)
      //  for all visitors check
      // if (this.forAllVisitors) {
      //   dataSend.data.publishNo = this.publishNo
      // }
      getWidgetData(dataSend)
        .then((response: any) => {
          if (response.data.code === 100) {
            this.$set(
              gridItem.chartOptions,
              'value',
              response.data.result.extraData.chartOptions.value
            )
            this.$set(gridItem, 'isLoading', false)
          }
        })
        .catch(() => {
          this.$set(gridItem, 'isLoading', false)
        })
    } else {
      console.error('call error: getTableData must be used by table')
    }
  }

  public saveText() {
    if (
      this.panelWidth ||
      this.panelTop ||
      this.panelHeight ||
      this.panelRight
    ) {
      KGraphStore.setDashboardText({
        ...this.textList[0],
        width: this.panelWidth ? this.panelWidth : this.textList[0].width,
        height: this.panelHeight ? this.panelHeight : this.textList[0].height,
        top: this.panelTop ? this.panelTop : this.textList[0].top,
        right: this.panelRight ? this.panelRight : this.textList[0].right,
      })
    }
  }
  //  删除之后，需要点击保存按钮
  public deleteText(item: any) {
    KGraphStore.deleteTextItem(item)
  }

  public handleTextChange(item: any, value: string) {
    this.$set(item.chartOptions, 'value', value)
  }

  public handleTextSize(gridItem: any, parameter: any) {
    // this.$set(gridItem, 'width', parameter.width)
    // this.$set(gridItem, 'height', parameter.height)

    KGraphStore.setDashboardText({
      ...gridItem,
      width: parameter.width,
      height: parameter.height,
    })
    //  通过点击进行保存
  }

  public handleTextPos(gridItem: any, parameter: any) {
    // this.saveTextValue(gridItem)
    KGraphStore.setDashboardText({
      ...gridItem,
      top: parameter.top,
      right: parameter.right,
    })
    //  通过点击进行保存
  }

  public async handleTextBlur(gridItem: any) {
    this.$set(gridItem, 'draggable', true)
    // this.$nextTick(() => {
    //   this.dataStore.setCurrentGridItem(gridItem)
    // })
    //  Save text value by update Widget
    this.saveTextValue(gridItem)
  }

  private async saveTextValue(gridItem: any) {
    //  Save text value by update Widget
    gridItem.chartOptions.value = this.transferTextValue(
      gridItem.chartOptions.value
    )
    const parameters = buildSaveParameters(gridItem)
    if (KGraphStore.dashboardId) {
      parameters.data.dashboardId = KGraphStore.dashboardId
    }
    if (gridItem.widgetId) {
      await updateWidget(parameters) // 保存text widget
    }
  }

  // 若value 中存在公式，则替换为公式的值
  public transferTextValue(value: string) {
    const htmlTemplate = value

    const domparser = new DOMParser()
    const parsedDom = domparser.parseFromString(htmlTemplate, 'text/html') // [object HTMLDocument]

    const elements = parsedDom.querySelectorAll('.formula-style')
    if (elements.length > 0) {
      //  replace text with data-result value with innerText
      //  remove .formula-style, add result-style
      elements.forEach((item: any) => {
        item.textContent = item.getAttribute('data-result')
        item.classList.remove('formula-style')
        item.classList.add('result-style')
      })
    }

    const html = parsedDom.documentElement.outerHTML
    const pre = html.indexOf('<body>') + '<body>'.length
    const after = html.indexOf('</body>')

    return html.slice(pre, after)
  }

  public get knowledgeGraphMode(): string {
    return KGraphStore.knowledgeGraphMode
  }

  public get entityList() {
    if (this.knowledgeGraphMode === 'preview') {
      return []
    }
    return KGraphStore.entityPanelList || []
  }

  public get clickFeatureInfo() {
    return KGraphStore.clickLayerFeatureInfo
  }

  public get selectedEntityNode() {
    return KGraphStore.selectedEntityNode
  }

  public clickInfo: any = null //  记录点击的实体信息，主要是id

  @Watch('clickFeatureInfo', { deep: true })
  public handleClickedFeatrueInfo(newValue: Array<any>) {
    if (newValue === null) {
      return
    }
    //  非预览态监听点击事件
    if (
      this.knowledgeGraphMode !== 'preview' &&
      newValue.length > 0 &&
      ['circle', 'fill'].includes(newValue[0].layer.type)
    ) {
      //  排除点击线数据
      if (
        this.clickInfo === null ||
        (this.clickInfo && this.clickInfo.id !== newValue[0].id)
      ) {
        //  save opened entity before empty
        this.saveOpenEntityInfo()
        //  empty store save
        // KGraphStore.setEntityPanelList([])
        // KGraphStore.setGridList([])
        this.clickInfo = {
          id: newValue[0].id,
        }

        const target = newValue[0]
        KGraphStore.queryEntityInfo({
          projectId: this.projectId,
          vid: target.properties.id,
        })
        //  set select entity panel
        KGraphStore.setSelectEntityPanel(newValue[0].id)
        //  set focus box
        KGraphStore.setFocusBoxg('entity')
      }
    }
  }
  //  关闭并保存打开的实体面板
  public saveOpenEntityInfo() {
    if (this.entityList.length > 0) {
      const target = cloneDeep(this.entityList)
      // eslint-disable-next-line no-restricted-syntax
      for (const entity of target) {
        const instance: any = this.$refs[`entity-${entity.id}`] as Vue
        instance[0].closeEntity()
      }
    }
  }

  public async resetEntity(entityId: number) {
    const response = await resetPanelInfoByVid({
      data: {
        projectId: this.projectId,
        vid: entityId,
      },
    })

    if (response.data.code === 100) {
      const { basic, layout, id } = response.data.result
      const entityData: any = {
        id,
        layout,
        basic,
      }
      KGraphStore.replaceEntityPanelList(entityData)
    }
  }
  /**
   * 关闭实体面板
   * @param dataInfo 实体数据以及是否需要保存flag
   */
  public async closeEntityPanel(dataInfo: any) {
    const { entityData, shouldSave } = dataInfo
    const entityId = entityData.id
    this.clickInfo = null

    if (shouldSave) {
      //  case: 需要保存实体信息
      const target = this.entityList.find(
        (item: any) => item.layout.id === entityId
      )
      const response = await updatePanelInfoByPanelId({
        data: {
          id: target.id,
          layout: entityData,
          basic: target.basic,
        },
      })
      if (response.data.code === 100) {
        this.removeEntityRelatedData(entityData)
      } else {
        this.$message.error('保存实体信息失败')
      }
    } else {
      this.removeEntityRelatedData(entityData)
    }
  }

  /**
   * 关闭实体面板 需要清除实体相关数据
   * @param entityData 实体数据
   */
  public removeEntityRelatedData(entityData: any) {
    KGraphStore.removeEntityPanelList(entityData)
    //  delete gridList
    KGraphStore.removeGridList(entityData.id)
    //  重置点击实体的存储
    // KGraphStore.setClickLayerFeatureInfo(null)
  }

  public saveAndOpenEntity(dataInfo: any) {
    this.saveOpenEntityInfo()
    this.openOtherEntity(dataInfo)
  }

  /**
   * 是否该实体 已经有实体面板
   * @param entityName 实体名称
   */
  public checkIfEntityExist(entityName: string) {
    // eslint-disable-next-line no-restricted-syntax
    for (const item of this.entityList) {
      if (item.layout.name === entityName) {
        return true
      }
    }
    return false
  }

  public async openOtherEntity(dataInfo: any) {
    const { id, searchKey } = dataInfo
    // 若实体面板已经打开，则不再重复打开
    if (this.checkIfEntityExist(searchKey)) {
      return
    }
    //  打开其他实体面板，并设置为选中的面板
    KGraphStore.setSelectEntityPanel(id)
    //  set focus box
    KGraphStore.setFocusBoxg('entity')
    //  query be api
    const response = await KGraphStore.queryEntityInfo({
      projectId: this.projectId,
      vid: id,
    })
    const { layout } = response.data.result

    const geoInfo: any = {}
    // eslint-disable-next-line no-restricted-syntax
    for (const item of layout.items) {
      // eslint-disable-next-line no-restricted-syntax
      for (const child of item.children) {
        if (['纬度', '经度'].includes(child.attrName)) {
          geoInfo[child.attrName] = child.attrValue
        }
      }
    }

    if ('纬度' in geoInfo) {
      KGraphStore.setSelectedEntityNode({
        id: String(id),
        attrs: geoInfo,
      })
    } else if (searchKey && searchKey.length > 0) {
      //  for 面数据 focus
      const polygonId: any = KGraphStore.gisInfo?.entityLayerId[1]
      const entityDataFeatures =
        KGraphStore.layerData[polygonId].geojson.features
      const searchResult = entityDataFeatures.filter((item: any) => {
        const reg = new RegExp(searchKey, 'gi')
        const { label } = item.properties
        if (label) {
          return label.match(reg)
        }
        return false
      })
      if (searchResult.length > 0) {
        const searchResultFirstGeometryCoordinates: any =
          searchResult[0].geometry.coordinates[0][0][0]
        this.mapBox.easeTo({
          center: searchResultFirstGeometryCoordinates,
          zoom: 14,
          duration: 1000,
        })
        this.focusEntityByLngLatAndId(searchResult[0].id)
      } else {
        //  for binjiangqu case
        this.searchGis(searchKey)
      }
    }
  }

  public async searchGis(name: string) {
    const parameters: any = {
      postStr: {
        searchWord: name,
        searchType: '1',
        needSubInfo: false,
        needAll: false,
        needPolygon: true,
        needPre: true,
      },
      tk: key,
    }
    const response: any = await KGraphStore.searchAdministrative(parameters)
    if (response.data.returncode === '100') {
      const { bound } = response.data.data[0]
      if (bound) {
        const [lat1, lnt1, lat2, lnt2] = bound.split(',')
        const boundArray: any[] = [
          [lat1, lnt1],
          [lat2, lnt2],
        ]

        this.mapBoxFitBoundsMixin(boundArray)
        // this.$emit('fit-bounds', boundArray)
      }
    }
  }

  // 暂时使用
  public currentMapTypeStyle: any = {
    mapType: 1,
    tileStyle: 1,
  }
  // map 全局 loading
  public get mapLoading(): boolean {
    return KGraphStore.mapLoading
  }
  // 所有图层
  public get layerList() {
    return KGraphStore.layerList
  }
  // dashboard gis info
  public get gisInfo(): GisInfo | null {
    return KGraphStore.gisInfo
  }
  // 实体图层 id
  public get entityLayerId(): any {
    //  会有两个id ，一个是点数据 id , 一个是面数据 id
    return KGraphStore.gisInfo?.entityLayerId[0]
  }
  // 实体关系图层 id
  public get relationLayerId(): any {
    return KGraphStore.gisInfo?.relationLayerId
  }
  public get gisInfoMapTypeStyle(): { mapType: number; tileStyle: number } {
    return {
      mapType: this.gisInfo?.mapType || 1,
      tileStyle: this.gisInfo?.tileStyle || 1,
    }
  }
  // 可见图层（带有排列顺序）
  public get layerIndexOrderOfVisible() {
    return KGraphStore.layerList
      .filter((item: any) => item.visible === 1)
      .sort((layer1: any, layer2: any) => layer1.order - layer2.order)
  }

  // public get mode() {
  //   return KGraphStore.mode
  // }

  public mounted() {
    this.projectId = Number(this.$route.params.id)
    if (this.layerList) {
      KGraphStore.setMapLoading(true)
    }
  }

  /**
   * 存在的图层
   */
  public exitLayerIds() {
    const exitLayerIds: string[] = []
    if (this.mapBox) {
      KGraphStore.layerList.forEach((layer: any) => {
        const mapLayer = this.mapBox.getLayer(String(layer.id))
        if (mapLayer) {
          exitLayerIds.push(String(layer.id))
        }
      })
    }
    return exitLayerIds
  }

  // Dashboard 更新
  public get updateDashboardTime() {
    return KGraphStore.updateDashboardTime
  }
  @Watch('updateDashboardTime')
  public onUpdateDashboardTimeChange() {
    KGraphStore.setMapLoading(true)
    this.queryDashboardData()
    KGraphStore.setEntityPanelList([])
  }

  //  gisMap upadte
  public get updateGisMap() {
    return KGraphStore.updateGisMapTime
  }
  @Watch('updateGisMap')
  public onUpdateGisMap() {
    KGraphStore.setMapLoading(true)
    this.updateMap()
    KGraphStore.setEntityPanelList([])
  }
  // 删除加载的知识图谱触发清除画布
  public get clearDashboardTime() {
    return KGraphStore.clearDashboardTime
  }
  @Watch('clearDashboardTime')
  public onClearDashboardTimeChange() {
    this.clearLayer()
    if (this.mapBox) {
      this.mapBox.remove()
    }
    this.mapBox = null
    KGraphStore.setDashboardInfo(null)
  }

  /**
   * 左侧知识图谱选择实体
   * @param entityNode
   */
  @Watch('selectedEntityNode', { deep: true })
  public selectedEntityNodeChange(entityNode: any) {
    if (this.knowledgeGraphMode === 'preview') {
      return
    }
    // 聚焦实体，
    const { attrs } = entityNode
    this.mapBox.easeTo({
      center: [attrs['经度'], attrs['纬度']],
      zoom: 14,
      duration: 1000,
    })
    this.focusEntityByLngLatAndId(entityNode.id)
  }

  /**
   * 高亮实体
   */
  public focusEntityByLngLatAndId(entityId: any) {
    const entityIds: any[] = entityId ? [entityId] : []
    selectedSetFeatureState(this.mapBox, this.entityLayerId, entityIds)
  }

  /**
   * 搜索实体名称 - jinhui 根据geojson 搜索
   */
  public searchEntity(searchKey: string) {
    if (!searchKey) {
      this.$message.warning('请输入搜索关键字')
      this.focusEntityByLngLatAndId(null)
      return
    }
    const entityDataFeatures =
      KGraphStore.layerData[this.entityLayerId].geojson.features
    const searchResult = entityDataFeatures.filter((item: any) => {
      const reg = new RegExp(searchKey, 'gi')
      const { label } = item.properties
      if (label) {
        return label.match(reg)
      }
      return false
    })
    if (searchResult.length > 0) {
      const searchResultFirstGeometryCoordinates: any =
        searchResult[0].geometry.coordinates
      this.mapBox.easeTo({
        center: searchResultFirstGeometryCoordinates,
        zoom: 14,
        duration: 1000,
      })
      this.focusEntityByLngLatAndId(searchResult[0].id)
    } else {
      this.focusEntityByLngLatAndId(null)
      this.$message.warning('没有搜索到相关实体!')
    }
  }

  /**
   * 查询项目信息
   */
  public async queryDashboardData() {
    const response: any = await KGraphStore.queryKGDashBoard({
      projectId: this.projectId,
    })
    if (response.data.code === 100) {
      await this.updateMap()
    } else {
      this.$message.error('错误')
    }
  }

  /**
   * 更新地图(地图配置的更改)
   * 地图类型
   * 底图样式
   * ...
   */
  public updateSetting({ type, value }: any) {
    if (this.mapBox) {
      switch (type) {
        case 'mapTypeStyle': {
          // 底图切换
          const { mapType, tileStyle } = value
          if (
            this.gisInfo?.mapType !== mapType ||
            this.gisInfo?.tileStyle !== tileStyle
          ) {
            this.changeMapTypeStyle(mapType, tileStyle)
            this.$nextTick(() => {
              this.initAddLayerOfVisible()
            })
          }
          break
        }
        case 'scale': // 比例尺
          this.addOrRemoveScaleControlMixin(value)
          break
        case 'legend': // 图例
          break
        default:
          break
      }
    }
  }

  /**
   * 更换地图样式
   * @param mapType 地图类型 1 - 高德
   * @param tileStyle 底图样式
   * @private
   */
  private changeMapTypeStyle(mapType: number = 1, tileStyle: number = 1) {
    this.mapBox.setStyle(getMapTilesByMapTypeAndStyle(mapType, tileStyle)) // 修改地图样式
    KGraphStore.setDashboardGisInfo({ key: 'tileStyle', value: tileStyle })
    KGraphStore.setDashboardGisInfo({ key: 'mapType', value: mapType })
    this.currentMapTypeStyle.mapType = mapType
    this.currentMapTypeStyle.tileStyle = tileStyle
  }

  /**
   * 更新地图
   */
  public async updateMap() {
    if (this.mapBox) {
      this.clearLayer() // 清除图层
      const { currentMapTypeStyle } = this
      // 配置 是否需要更换底图
      if (
        currentMapTypeStyle &&
        currentMapTypeStyle.tileStyle !== this.gisInfo?.tileStyle
      ) {
        this.changeMapTypeStyle(this.gisInfo?.mapType, this.gisInfo?.tileStyle)
        this.$nextTick(() => {
          this.initAddLayerOfVisible()
        })
      } else {
        this.initAddLayerOfVisible()
      }
    } else {
      this.$nextTick(() => {
        this.initMap()
        this.mapResizeMixin()
      })
    }
    //  get text value
    this.$nextTick(() => {
      if (this.textList.length > 0) {
        //  get text value;
        this.textList.forEach((item: any) => {
          this.getTextData(item)
        })
      }
    })
  }

  /**
   * 清空图层， 重新画新的图层
   */
  public clearLayer() {
    if (this.mapBox) {
      KGraphStore.layerList.forEach((layer: any) => {
        const checkLayerLabelIsExist: boolean = !!this.mapBox.getLayer(
          `label-${layer.id}`
        )
        if (checkLayerLabelIsExist) {
          this.mapBox.removeLayer(`label-${layer.id}`)
        }
        const checkLayerArrowIsExist: boolean = !!this.mapBox.getLayer(
          `arrow-${layer.id}`
        )
        if (checkLayerArrowIsExist) {
          this.deleteLayerArrowByIds(layer.id)
        }
        const checkLayerIsExist: boolean = !!this.mapBox.getLayer(`${layer.id}`)
        if (checkLayerIsExist) {
          this.mapBox.removeLayer(`${layer.id}`)
          this.mapBox.removeSource(`source-${layer.id}`)
        }
      })
    }
  }

  /**
   * 地图复位
   */
  private resetMap() {
    this.resetMapMixin(this.layerList, KGraphStore.layerData)
  }

  /**
   * 保存关闭实体panel & 清除map 选中
   */
  private clearSelectedState() {
    this.saveOpenEntityInfo()
    clearSelectedFeatureState(this.mapBox) // 清除选中的要素
  }

  /**
   * 实例化地图
   */
  public async initMap() {
    KGraphStore.setMapLoading(true)
    this.initMapBoxMixin(
      this.gisInfo?.mapType,
      this.gisInfo?.tileStyle,
      this.idName
    ) // 实例化 mapbox gl
    this.mapBox.on('load', () => {
      this.initAddLayerOfVisible() // 初始化 加载可视状态的图层
      this.initAndUpdateMapboxEvent() // 地图事件  当删除一些图层时需要更新
    })
    KGraphStore.setMapboxInstance(this.mapBox) //  store 存储 mapbox 实例
    this.addOrRemoveScaleControlMixin(true)
    this.addCoordinatesControlMixin()
    // this.addNavigationControlMixin(this.resetMap)
  }

  /**
   * 初始化/更新 地图事件
   */
  public initAndUpdateMapboxEvent() {
    this.initAndUpdateMapboxOnClickEvent()
    this.initAndUpdateMapboxOnMouseEvent()
  }

  /**
   * 地图点击事件的初始化与更新
   */
  public initAndUpdateMapboxOnClickEvent() {
    this.mapBox.off('click', ($event: any) => this.mapboxClickEvent($event))
    this.mapBox.on('click', ($event: any) => this.mapboxClickEvent($event))
  }

  /**
   * 地图点击事件
   * @param $event
   */
  public mapboxClickEvent($event: any) {
    const features = this.mapBox.queryRenderedFeatures($event.point, {
      layers: this.exitLayerIds(),
    })
    if (features.length > 0) {
      KGraphStore.setCurrentToolBarAction(null)
      KGraphStore.setClickLayerFeatureInfo(features)
      // 高亮点击的要素
      const { id, layer } = features[0]
      const layerId: string = layer.id
      clearSelectedFeatureState(this.mapBox)
      selectedSetFeatureState(this.mapBox, layerId, [id])
    } else if (this.currentToolBarAction !== 'gisSetting') {
      //  当项目锁定的时候，不会显示gis 设置modal
      if (this.isLock) {
        return
      }
      KGraphStore.setCurrentToolBarAction('gisSetting')
      //  切换为gisSetting 需要关闭打开的panel
      this.saveOpenEntityInfo()
      clearSelectedFeatureState(this.mapBox) // 清除选中的要素
    } else if (this.currentToolBarAction === 'gisSetting') {
      KGraphStore.setCurrentToolBarAction(null)
      clearSelectedFeatureState(this.mapBox) // 清除选中的要素
    }
  }

  public initAndUpdateMapboxOnMouseEvent() {
    this.mapBox.off('mousemove', ($event: any) => this.globalMouseEvent($event))
    this.mapBox.on('mousemove', ($event: any) => this.globalMouseEvent($event))
  }

  /**
   * 全局鼠标事件
   * TODO 需要修改
   */
  public globalMouseEvent($event: any) {
    const features = this.mapBox.queryRenderedFeatures($event.point, {
      layers: this.exitLayerIds(),
    })
    if (features.length > 0) {
      const featureFirst = features[0]
      // const layerId = featureFirst.layer.id
      // TODO hover 高亮
      globalHoverSetFeatureState(this.mapBox, featureFirst, 'mousemove')
      $event.preventDefault()
      this.mapBox.getCanvas().style.cursor = 'pointer'
      const coordinates = featureFirst.geometry.coordinates.slice()
      const currentFeatureProperty: any = featureFirst.properties
      //  remove useless property
      const propertyInShow = Object.fromEntries(
        Object.entries(currentFeatureProperty).filter(
          ([propertyKey]) =>
            !['color', '纬度', '经度', 'id', 'name', '简称'].includes(
              propertyKey
            )
        )
      )
      // delete currentFeatureProperty._record_id_
      const propertyKey: any[] = Object.keys(propertyInShow)
      if (propertyKey.length > 0) {
        let attributesDom = ''
        propertyKey.forEach((item: any) => {
          attributesDom += `<div class="attr-key-value">${item}: ${currentFeatureProperty[item]}</div>`
        })
        while (Math.abs($event.lngLat.lng - coordinates[0]) > 180) {
          coordinates[0] += $event.lngLat.lng > coordinates[0] ? 360 : -360
        }
        this.mapboxPopup
          .setLngLat($event.lngLat)
          .setHTML(`<div>${attributesDom}</div>`)
          .setMaxWidth('300px')
          .addTo(this.mapBox)
      }
    }
  }

  /**
   * 地图图层添加鼠标事件 (高亮， tooltip)
   */
  public addMouseEventToMapBoxByLayerId(layerId: string) {
    this.mapBox.on('mouseleave', layerId, ($event: any) => {
      this.mapBox.getCanvas().style.cursor = ''
      this.mapboxPopup.remove()
      hoverSetFeatureState(this.mapBox, $event, layerId, 'mouseleave')
    })
  }

  /**
   * 通过图层 Id 更新图层
   */
  public updateLayerAndSourceByLayerId(layer: any) {
    this.mapBox.removeLayer(`${layer.id}`)
    this.mapBox.removeSource(`source-${layer.id}`)
    this.checkLayerDataExitAndAddLayer(layer.id, layer.type, layer.arrow)
  }

  /**
   * 修改图层顺序
   * @param order
   */
  public updateLayerOrderEmit(order: any) {
    // 先修改 store 中的数据 再更新图层
    KGraphStore.editLayerOrder(order)
    this.updateMapLayerOrder(this.layerIndexOrderOfVisible)
  }

  /**
   * 修改图层显隐状态
   * @param layer
   */
  public toggleLayerVisibleEmit(layer: any) {
    if (!layer) {
      return
    }
    KGraphStore.editLayerVisible({ [layer.id]: layer.visible })
    // 检测数据的存在
    const layerExit: boolean = !!this.mapBox.getLayer(String(layer.id))
    if (layerExit) {
      // 修改图层的显隐状态
      this.editLayerVisiblePropertyByLayerId(String(layer.id), layer.visible)
      // 该图层的 label 图层
      if (this.nameSet !== 'hide') {
        this.editLayerVisiblePropertyByLayerId(
          `label-${layer.id}`,
          layer.visible
        )
      }
      // 该图层的 arrow 图层
      if (layer.arrow) {
        this.editLayerVisiblePropertyByLayerId(
          `arrow-${layer.id}`,
          layer.visible
        )
      }
      this.initAndUpdateMapboxEvent()
    } else if (layer.visible === 1) {
      this.checkLayerDataExitAndAddLayer(layer.id, layer.type, layer.arrow)
    }
  }
  /**
   * 短暂显示常驻图层
   * @param layerId 图层 id
   * @param fixed
   */
  public showFixedLineLayer(layerId: string) {
    const currentFixedLayer: any = KGraphStore.allLayerList.find(
      (layer: any) => layer.id === layerId
    )
    if (currentFixedLayer) {
      const { id, type, arrow } = currentFixedLayer
      this.checkLayerDataExitAndAddLayer(id, type, arrow, true)
    }
  }

  /**
   * 删除短暂显示的常驻图层
   * @param fixedId
   */
  public removeFixedLineLayer(fixedId: string) {
    this.deleteLayerArrowByIds(fixedId)
    this.deleteLayerByIds([fixedId])
  }

  /**
   * 修改图层为常驻状态
   * @param layerId 图层 id
   * @param fixed
   */
  public toggleLayerFixedEmit(layerId: string, fixed: boolean) {
    const currentFixedLayer: any = KGraphStore.allLayerList.find(
      (layer: any) => layer.id === layerId
    )
    if (currentFixedLayer) {
      const { id, type, arrow } = currentFixedLayer
      this.checkLayerDataExitAndAddLayer(id, type, arrow)

      // 修改图层为常驻状态 store
      KGraphStore.setDashboardLayerFixed({ id: layerId, fixed })
    } else {
      this.$message.warning('该图层不存在, 请确认!')
    }
  }

  /**
   * 取消常驻图层, 删除图层
   * @param fixedId
   */
  public cancelLayerFixedEmit(fixedId: any) {
    KGraphStore.setDashboardLayerFixed({ id: fixedId, fixed: false })
    this.deleteLayerByIds([fixedId])
  }

  /**
   * 更新图层层级关系
   * visibleLayers 已显示的图层
   * includeLayerId 需要额外增加的图层
   */
  public updateMapLayerOrder(visibleLayers: any, includeLayerId?: string) {
    let needSortLayer: any[] = visibleLayers
    if (includeLayerId) {
      needSortLayer = KGraphStore.layerList.filter(
        (item: any) => item.visible === 1 || item.id === includeLayerId
      )
    }
    this.$nextTick(() => {
      for (let i: number = 0; i <= needSortLayer.length - 2; i += 1) {
        const layerId1: string = `${needSortLayer[i].id}`
        const layerId2: string = `${needSortLayer[i + 1].id}`
        const layer1: any = this.mapBox.getLayer(layerId1)
        const layer2: any = this.mapBox.getLayer(layerId2)
        // 各图层对 label 的位置排序
        const layer1LabelId: string = `label-${layerId1}`
        const layer1Label: any = this.mapBox.getLayer(layer1LabelId)
        if (layer1Label) {
          this.mapBox.moveLayer(layerId1, layer1LabelId)
        }

        if (layer1 && layer2) {
          // beforeId 要在之前插入新图层的现有图层id。查看地图时， 图层将出现在图层下方 。如果省略，则图层将附加到图层数组的末尾并出现在地图上的所有其他图层之上。
          this.mapBox.moveLayer(layerId2, layerId1)
        }
      }
      // this.addMouseEventToMapBoxByLayerId(`${needSortLayer[0].id}`)
    })
  }

  /**
   * 检查已有 图层 是否是显示状态 添加图层
   */
  public initAddLayerOfVisible() {
    this.initAddEmptyMixin()
    // 可视状态图层
    const visibleLayers: any[] = this.layerList.filter(
      (layer: any) => layer.visible === 1
    )
    if (visibleLayers.length > 0) {
      this.renderLayerAfterGetLayerData(visibleLayers)
      this.updateMapLayerOrder(this.layerIndexOrderOfVisible) // 调整图层层级
    }
    KGraphStore.setMapLoading(false)
  }

  /**
   * 渲染指定多个图层（先获取数据）
   * @param layers
   */
  public async renderLayerAfterGetLayerData(layers: any[]) {
    if (layers.length > 0) {
      KGraphStore.setMapLoading(true)
      await this.getLayerDataByLayers(layers) // 获取所有可视图层的 渲染数据
      this.renderVisibleLayer(layers) // 渲染各可视状态图层
      this.resetMap() // 基于图层包围盒复位
      KGraphStore.setMapLoading(false)
    }
  }

  /**
   * 获取多个图层的渲染数据
   */
  public async getLayerDataByLayers(layers: any[]) {
    // 图层数据不存在的图层， 避免重复获取图层数据
    // const dataNonExistentLayer: any[] = layers.filter((layer: any) => {
    //   return !KGraphStore.layerData[layer.id]
    // })
    if (layers.length > 0) {
      const requests = layers.map(async (layer: any) => {
        return this.queryLayerDataByLayerId(layer.id)
      })
      await Promise.allSettled(requests)
    }
  }

  /**
   * 获取图层数据
   * @param layerId
   * @param fixedLayer 是否绘制常驻图层
   */
  public async queryLayerDataByLayerId(
    layerId: number,
    fixedLayer: boolean = false
  ) {
    const parameters: any = {
      projectId: this.projectId,
      layerId,
      kGraph: !fixedLayer || true, //  无论是否fixedLayer，都是true
    }
    const response: any = await KGraphStore.queryLayerDataById(parameters)
    return response
  }

  /**
   * 渲染所有有数据的图层
   * @private
   */
  private renderVisibleLayer(visibleLayers: any[]) {
    visibleLayers.forEach((layer: any) => {
      const beforeId: string = this.getLayerBeforeId(layer.id) // 获取图层 beforeId
      this.addLayerToMap(layer.id, layer.type, beforeId, layer.arrow)
    })
  }

  /**
   * 获取 定位图层位置 beforeId， 用于添加图层是定位
   * @private
   */
  private getLayerBeforeId(layerId: number) {
    const visibleLayers: any[] = this.layerList.filter(
      (layer: any) => layer.visible === 1
    )
    const index: number = visibleLayers.findIndex(
      (layer: any) => layer.id === layerId
    )
    let beforeId: string | null = 'base-empty-layer'
    if (index > 0 && visibleLayers[index - 1].id) {
      const layerIdString: string = String(visibleLayers[index - 1].id)
      const beforeIdExist: boolean = !!this.mapBox.getLayer(layerIdString)
      beforeId = beforeIdExist ? layerIdString : 'base-empty-layer'
    }
    return beforeId
  }

  /**
   * 添加图层之前的获取图层数据， 并添加图层
   * @param layerId 图层id
   * @param layerType 图层类型
   * @param arrow  是否有箭头
   * @param fixedLayer 是否绘制常驻图层
   */
  public async checkLayerDataExitAndAddLayer(
    layerId: number,
    layerType: string,
    arrow: boolean = false,
    fixedLayer: boolean = false
  ) {
    // 定位图层位置
    const beforeId: string = this.getLayerBeforeId(layerId)
    if (KGraphStore.layerData[layerId]) {
      this.addLayerToMap(layerId, layerType, beforeId, arrow)
      return
    }
    // 获取图层数据
    const response: any = await this.queryLayerDataByLayerId(
      layerId,
      fixedLayer
    )
    if (response.data.code === 100) {
      this.addLayerToMap(layerId, layerType, beforeId, arrow)
    } else {
      this.$message.error('错误')
    }
  }

  /**
   * 添加图层
   * @param layerId 图层id
   * @param layerType 图层类型
   * @param beforeId 图层 beforeId
   * @param arrow 线图层是否带箭头
   */
  public addLayerToMap(
    layerId: number,
    layerType: string,
    beforeId: string,
    arrow: boolean = false
  ) {
    const layerDataInfo: any = KGraphStore.layerData[layerId]
    if (!layerDataInfo) {
      return
    }
    if (layerType === 'circle') {
      this.addPointMixin(layerDataInfo, layerId, beforeId)
    } else if (layerType === 'line') {
      this.addLineMixin(layerDataInfo, layerId, beforeId, arrow)
    } else if (layerType === 'fill') {
      this.addPolygonMixin(layerDataInfo, layerId, beforeId)
    }
    // 是否显示实体名称 label
    // const isEntityLayer: boolean = layerId === this.entityLayerId
    if (this.nameSet !== 'hide') {
      // 增加第二个参数，用于显示简称
      this.addLabelMixin(layerId, this.nameSet === 'abbr' ? '简称' : 'label')
    }
    this.addMouseEventToMapBoxByLayerId(String(layerId))
  }

  /**
   * 当前 激活的工具栏项
   */
  public get currentToolBarAction() {
    return KGraphStore.currentToolBarAction
  }

  public get currentGridItem() {
    return KGraphStore.currentGridItem?.visible ?? false
  }

  /**
   * toolBar 行为动作
   */
  @Watch('currentToolBarAction')
  public watchCurrentToolBarAction(action: any) {
    switch (action) {
      // case 'gisPanVisible': // 平移
      //   this.gisMapPan()
      //   break
      // case 'gisBoxSelectVisible': // 框选
      //   this.gisSelectBox()
      //   break
      default:
        this.mapBox.dragPan.enable()
        break
    }
  }

  /**
   * 改变实体标注显示形式
   * @param checked
   */
  public changeNameSet(type: string) {
    KGraphStore.setNameSetting(type)
    this.toggleLayerLabelByLayers(type)
  }

  /**
   * 改变是否与左侧可视化联动
   * @param checked
   */
  public changeInterGraph(checked: boolean) {
    KGraphStore.setInteractSetting(checked)
  }

  /**
   * 修改图层的 label 显示
   * @param visible hide / abbr / full
   */
  public toggleLayerLabelByLayers(visible: string) {
    // 可视状态图层
    const visibleLayers: any[] = this.layerList.filter(
      (layer: any) => layer.visible === 1
    )
    visibleLayers.forEach((layer: any) => {
      // 目前显示实体图层
      // if (this.entityLayerId !== layer.id) {
      //   return
      // }
      const labelLayerId: string = `label-${layer.id}`
      const labelLayerExist: boolean = !!this.mapBox.getLayer(labelLayerId)
      if (labelLayerExist && visible === 'hide') {
        this.editLayerVisiblePropertyByLayerId(labelLayerId, 0)
      } else {
        //  remove first then add
        this.mapBox.removeLayer(labelLayerId)
        this.addLabelMixin(layer.id, visible === 'abbr' ? '简称' : 'label')
      }
    })
  }

  public get allLayerList(): any[] {
    return KGraphStore.allLayerList
  }

  /**
   * 清除已有的所有图层
   */
  public clearMapLayer() {
    this.allLayerList.forEach((layer: any) => {
      // 线图层有可能带箭头
      if (layer.type === 'line') {
        this.deleteLayerArrowByIds(layer.id)
      }
      this.deleteLayerByIds(layer.id)
    })
  }

  private destroyed() {
    if (this.drag) {
      this.drag.destroy()
    }
  }

  public closeSearch() {
    KGraphStore.setCurrentToolBarAction(null)
  }
}
</script>

<style scoped lang="less">
.gis-map-component {
  height: 100%;
  position: relative;

  .map-class {
    height: 100%;
    position: relative;
    width: 100%;
  }

  .layer-loading {
    align-items: center;
    background-color: rgba(87, 96, 230, 0.1);
    display: flex;
    height: 100%;
    justify-content: center;
    left: 0;
    position: absolute;
    top: 0;
    width: 100%;
    z-index: 100;
  }

  .editor-container {
    background: #fff;
    // border: 1px solid;
    display: flex;
    flex-direction: column;
    // height: 60px;
    // padding: 0 15px;
    position: absolute;
    right: 100px;
    top: 0;
    z-index: 1000;
    // z-index: 2010;
    /deep/ .editor-box {
      margin-top: 0;
    }

    /deep/ .tinymce-body {
      padding: 8px;
    }

    .text-editor {
      display: flex;
      flex-direction: column-reverse;
      position: relative;
    }
  }
}
</style>
